import type { Transferable, } from './gameObjects/interfaces';
import {
  Image,
  Text,
  Rectangle,
} from './gameObjects';
import {
  createEmptyTranformMatrix,
  matrixMultiply,
} from './utils';
import {
  fontStyleObjToStr,
} from './engine-utils';

type Transform = [number, number, number, number, number, number];

const calcRotateMatrix = (tGameObject: Transferable) => {
  const rotateMatrix: number[][] = createEmptyTranformMatrix();
  const radian = Math.PI / 180 * tGameObject.rotate;
  const sin = Math.sin(radian);
  const cos = Math.cos(radian);
  rotateMatrix[0][0] = cos;
  rotateMatrix[1][0] = sin;
  rotateMatrix[0][1] = -sin;
  rotateMatrix[1][1] = cos;

  return rotateMatrix;
}

/** calculate transform array */
const calcTransform = (tGameObject: Transferable): Transform => {
  const matrixs: number[][][] = [];

  const matrix: number[][] = createEmptyTranformMatrix();

  // scale
  matrix[0][0] = tGameObject.scale.x;
  matrix[1][1] = tGameObject.scale.y;

  // skewX
  const radianX = Math.PI / 180 * tGameObject.skew.x;
  const tanX = Math.tan(radianX);
  matrix[0][1] = tanX;

  // skewY
  const radianY = Math.PI / 180 * tGameObject.skew.y;
  const tanY = Math.tan(radianY);
  matrix[1][0] = tanY;

  // translate
  matrix[0][2] = tGameObject.position.x;
  matrix[1][2] = tGameObject.position.y;

  matrixs.push(matrix);

  // rotate
  if (tGameObject.rotate !== 0) {
    matrixs.push(calcRotateMatrix(tGameObject));
  }

  // origin
  const { x: originX, y: originY, } = tGameObject.origin;
  if (originX !== 0 || originY !== 0) {
    const originOffsetX = tGameObject.width * originX;
    const originOffsetY = tGameObject.height * originY;
    const originMatrix: number[][] = createEmptyTranformMatrix();
    originMatrix[0][2] = -originOffsetX;
    originMatrix[1][2] = -originOffsetY;
    matrixs.push(originMatrix);
  }

  if (matrixs.length === 1) {
    const m = matrixs[0];
    return [ m[0][0], m[1][0], m[0][1], m[1][1], m[0][2], m[1][2] ];
  } else if (matrixs.length > 1) {
    const m = matrixs.reduce((a, c) => matrixMultiply(a, c));
    return [ m[0][0], m[1][0], m[0][1], m[1][1], m[0][2], m[1][2] ];
  } else {
    return [ 1, 0, 0, 1, 0, 0 ];
  }
};

const getRealTransformMatrix = (matrix: DOMMatrix) => {
  const realTransformMatrix = createEmptyTranformMatrix();
  realTransformMatrix[0][0] = matrix.a;
  realTransformMatrix[1][0] = matrix.b;
  realTransformMatrix[0][1] = matrix.c;
  realTransformMatrix[1][1] = matrix.d;
  realTransformMatrix[0][2] = matrix.e;
  realTransformMatrix[1][2] = matrix.f;

  return realTransformMatrix;
};

export const drawImage = (canvasCtx: CanvasRenderingContext2D, image: Image) => {
  const t = calcTransform(image);
  canvasCtx.transform(t[0], t[1], t[2], t[3], t[4], t[5]);
  image.transformMatrix = getRealTransformMatrix(canvasCtx.getTransform());

  canvasCtx.drawImage(image.img, 0, 0, image.width, image.height);
};

export const drawText = (canvasCtx: CanvasRenderingContext2D, text: Text) => {
  const t = calcTransform(text);
  canvasCtx.transform(t[0], t[1], t[2], t[3], t[4], t[5]);
  text.transformMatrix = getRealTransformMatrix(canvasCtx.getTransform());

  canvasCtx.fillStyle = text.color;
  canvasCtx.font = fontStyleObjToStr(text.fontStyle);
  canvasCtx.textAlign = text.textAlign;
  canvasCtx.textBaseline = text.textBaseline;
  canvasCtx.fillText(text.text, 0, 0);
  if (text.stroke) {
    canvasCtx.strokeStyle = text.stroke;
    canvasCtx.strokeText(text.text, 0, 0);
  }
};

export const drawRectangle = (canvasCtx: CanvasRenderingContext2D, rect: Rectangle) => {
  const t = calcTransform(rect);
  canvasCtx.transform(t[0], t[1], t[2], t[3], t[4], t[5]);
  rect.transformMatrix = getRealTransformMatrix(canvasCtx.getTransform());

  if (rect.fillColor) {
    canvasCtx.fillStyle = rect.fillColor;
    canvasCtx.fillRect(0, 0, rect.width, rect.height);
  }
  if (rect.strokeColor) {
    canvasCtx.strokeStyle = rect.strokeColor;
    canvasCtx.strokeRect(0, 0, rect.width, rect.height);
  }
};
